package xorm

import (
	"fmt"
	"io"
	"os"

	// import 下划线（如：import _ hello/imp）的作用：当导入一个包时，该包下的文件里所有init()函数都会被执行，然而，有些时候
	// 我们并不需要把整个包都导入进来，仅仅是是希望它执行init()函数而已。这个时候就可以使用 import _ 引用该包。
	// 即使用【import _ 包路径】只是引用该包，仅仅是为了调用init()函数，所以无法通过包名来调用包中的其他函数。
	_ "github.com/go-sql-driver/mysql" // 这是mysql驱动包
	"github.com/go-xorm/xorm"
)

// 通过xorm.NewEngine方法创建一个数据库操作引擎，该方法需要两个参数：driveName和dataSourceName:
var driverName = "mysql"
var dataSourceName = "root:@(localhost:3306)/scrm?charset=utf8"

/*var driverName = "mysql"
var dataSourceName = "root:@(localhost:3306)/ecom_test?charset=utf8"*/

// 创建引擎,可以在一个程序中创建多个engine
var engine, _ = xorm.NewEngine(driverName, dataSourceName)

// 创建日志文件
var f, _ = os.Create("sql.log")

type User struct {
	Id       int
	Account  string
	Nickname string
}

func main() {
	/*
		    参考资料 : https://www.teakki.com/p/5a29f39e6689a6f62c1d4efb
			1.xorm安装 : 	go get github.com/go-xorm/xorm

			2.同其他对象关系映射框架一样，xorm也支持连接、操作多种数据库，包括：mysql、mymysql、postgres、tidb、sqlite、
			mssql、oracle(测试）。数据库驱动安装举例如下：
				Mysql: 		go get github.com/go-sql-driver/mysql

			3.
			设置自动同步结构体到数据库 xorm框架的engine数据库引擎，提供了engine.Sync()方法，允许开发者将自定义的结构体同步
			到数据库中。随着xorm框架不断更新和迭代，在Sync方法的基础上，又提供了Sync2方法，用于将结构体同步更新到数据库中。
			Sync2方法主要的特性是：自动地 => 检测和创建表/检测和新增表中的字段名/检测创建和删除索引/转换varchar字段类型到text
			字段类型/警告字段的默认值.
			err = engine.Sync2(new(model.Permission), new(model.City), new(model.Admin), new(model.AdminPermission), new(model.User))s

			在控制台打印出生成的SQL语句(默认false) : 			engine.ShowSQL(true)
			//在控制台打印调试信息(默认false):					engine.ShowDebug = true
			//在控制台打印错误信息(默认false):					engine.ShowError = true
			//在控制台打印警告信息(默认false):					engine.ShowWarn = true
			设置数据库最大连接数(默认false): 				engine.SetMaxOpenConns(10)
			设置最大空闲连接数量(默认是2): 					engine.SetMaxIdleConns(3)



			SQL的执行(手写SQL语句)
			　　1、查询一个string类型的sql，返回[]map[string][]byte类型的切片（查询）
				results, err := engine.Query("select * from user")

			　　2、执行一个string的sql，返回结果影响行数（增删改）
				affected, err := engine.Exec("update user set .... where ...")

			SQL的执行(利用框架自带的函数进行拼凑SQL语句)
				1、插入一个或者多个数据：
				affected, err := engine.Insert(&struct)	// INSERT INTO struct () values ()
				affected, err := engine.Insert(&struct1, &struct2)
				// INSERT INTO struct1 () values ()      和       INSERT INTO struct2 () values ()
				affected, err := engine.Insert(&sliceOfStruct)	// INSERT INTO struct () values (),(),()
				affected, err := engine.Insert(&struct1, &sliceOfStruct2)
				// INSERT INTO struct1 () values ()		 和		  INSERT INTO struct2 () values (),(),()

			　　2、从数据库里面查询一条记录：
				has, err := engine.Get(&user) 	// SELECT * FROM user LIMIT 1

				3、从数据库中查询多条记录：
				sliceOfStructs := new(Struct)
				err := engine.Find(sliceOfStructs)	// SELECT * FROM user

				4. 因为太多了,请自行查看资料	https://www.cnblogs.com/guhao123/p/4159688.html
	*/

	if engine != nil {
		engine.ShowSQL(true)
		engine.SetMaxOpenConns(10)
		engine.SetMaxIdleConns(3)

		defer f.Close()
		engine.SetLogger(xorm.NewSimpleLogger(io.MultiWriter(f, os.Stdout))) //同时保存到文件和在控制台中输出)

		/*
			1.手写CRUD sql语句 增删改查四大天王
		*/
		manualSql()

		/*
			2.非手写方式
		*/
		//unmanualSql()

		/*
			3.事务处理
			同其他数据库框架一样，xorm也是支持事物操作的。在有需要的批量操作数据时，事务的使用往往是必要的。在xorm中，
			事务操作和Session联系在一起。一共三个步骤，分别为：创建session对象，Begin表示事务开始执行，Commit表示提交事务。
			中间为具体的数据库操作。
		*/
		//transaction()

	}

}

func manualSql() {
	// 增删改查四种基本方法,区别是Insert,Delete,Update 的返回值是int64 类型的状态码,
	// Query返回值是[]map[string][]byte类型的切片,QueryString是[]map[string]string切片
	//insertResult, _ := engine.Insert("insert into app_menu values()")
	//deleteResult, _ := engine.Delete("delete from ...")
	//updateResult, _ := engine.Update("update ...")
	selectResult, _ := engine.QueryString("select * from app_menu limit 1")
	// 两种通用增删改原生SQL语句, Exec返回结果影响行数
	//engine.SQL(" select * from user where id = 1 and user_name = 'davie'")
	//affected, _ := engine.Exec("update user set .... where ...")
	//fmt.Println(insertResult, deleteResult, updateResult, selectResult, affected)
	fmt.Println(selectResult)
}

func unmanualSql() {
	user := new(User)
	user.Id = 1
	user.Account = "8858588"
	user.Nickname = "阿金测试"
	// 拼凑 where and or 条件
	b, _ := engine.Where(" id = ? ", user.Id).And(" id = ? ", user.Id).Or(" id = ? ", user.Id).Get(user)
	// Count(*)
	counts, _ := engine.Count(&user)
	engine.In("id", 1, 2, 3).Find(user)
	fmt.Println("我是b", b, counts)
}

func transaction() {
	// 1.创建事务
	session := engine.NewSession()
	defer session.Close()

	// 2.事务开始
	_ = session.Begin()

	// 3.事务提交
	_ = session.Commit()

	// 4.具体的XORM业务代码 实例
	// 实例化对象,
	user1 := User{Account: "8858588", Nickname: "阿金测试"}
	_, err1 := session.Insert(&user1)
	result, err2 := session.QueryString("select * from user where account = 8858588 and nickname = '阿金测试'")
	fmt.Println("查询的结果集:", result, "\n我是结果集的id : ", result[0]["id"])
	user2 := User{Nickname: "updated"}
	//Update(bean interface{}, condiBeans ...interface{})  bean是需要更新的bean, condiBeans是条件
	_, err3 := session.Where(" id = ? ", result[0]["id"]).Update(user2)
	_, err4 := session.Where(" id = ? ", result[0]["id"]).Update(user2)

	// 5.事务回滚
	if err1 != nil || err2 != nil || err3 != nil || err4 != nil {
		e := session.Rollback()
		fmt.Println(e)
		return
	}
}
